package com.bhh.myservlet.connector;

import com.bhh.myservlet.processor.ServletProcessor;
import com.bhh.myservlet.processor.StaticProcessor;
import com.bhh.myservlet.requestandresponse.Request;
import com.bhh.myservlet.requestandresponse.Response;
import lombok.extern.slf4j.Slf4j;

import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Set;

/**
 * @author bhh
 * @description 实现Connector
 * <p>
 * 生成Request 和 Response
 *
 * @date Created in 2021-04-15 12:45
 * @modified By
 */
@Slf4j
public class NIOConnector{

    private static final int DEFAULT_PORT = 8888;

    private ServerSocketChannel server;
    private int port;
    private Selector selector;

    public NIOConnector(int port) {
        this.port = port;
    }

    public NIOConnector() {
        this(DEFAULT_PORT);
    }

    // 步骤 :
    // 初始化 ServerSocketChannel
    // 初始化 Selector
    // 将ServerSocketChannel绑定accept事件到selector
    // 监听端口, 获取selectionKeys
    // 处理事件
    // 若是accept事件, 则获取socketChannel
    // 紧接着读取数据, 并进行返回, 一次链接后就断开

    public void start() {
        try {
            server = ServerSocketChannel.open();
            server.socket().bind(new InetSocketAddress(port));
            server.configureBlocking(false);

            selector = Selector.open();
            server.register(selector, SelectionKey.OP_ACCEPT);

            log.info("服务器成功启动, 监听端口 : " + port);

            while (true) {

                int select = selector.select();
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                for (SelectionKey selectionKey : selectionKeys) {
                    handlers(selectionKey);
                }
                selectionKeys.clear();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            close(server);
        }
    }

    private void handlers(SelectionKey selectionKey) throws IOException {
        if (selectionKey == null) {
            throw new RuntimeException("SelectionKey 为空");
        }
        // 监听链接事件
        if (selectionKey.isAcceptable()) {
            ServerSocketChannel server = (ServerSocketChannel) selectionKey.channel();
            SocketChannel client = server.accept();
            client.configureBlocking(false);

            client.register(selector, SelectionKey.OP_READ);
        }
        // 监听的读事件
        else if (selectionKey.isReadable()) {

            SocketChannel client = (SocketChannel) selectionKey.channel();
            selectionKey.cancel();
            client.configureBlocking(true);

            InputStream input = client.socket().getInputStream();
            OutputStream output = client.socket().getOutputStream();

            Request request = new Request(input);
            request.parse();

            Response response = new Response(output);
            response.setRequest(request);

            if (request.getUri().startsWith("/servlet/")) {
                ServletProcessor processor = new ServletProcessor();
                processor.process(request, response);
            } else {
                StaticProcessor processor = new StaticProcessor();
                processor.process(request, response);
            }

            close(client);
        }
    }

    /**
     * 关闭流
     *
     * @param closeable
     */
    public void close(Closeable closeable) {
        try {
            closeable.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
